#include <stdio.h>
#include <stdlib.h>
#include <wiiuse/wpad.h>

#include <malloc.h>
#include <ogcsys.h>
#include <gccore.h>
#include <ogc/ipc.h>
#include <stdarg.h>
#include <ctype.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <math.h>
#include <time.h>

#include <ogc/lwp.h>
#include <ogc/mutex.h>

#include "protocol.h"
#include "LocalClient.h"
#include "WiiTextArea.h"
#include "OnScreenKeyboard.h"

#include <fat.h>
#include <wiisprite.h>

#define NUM_THREADS 3

using namespace wsp;

int x_pos = 1;
int y_pos = 1;
s32 status = 0;

static void *xfb = NULL;
static GXRModeObj *rmode = NULL;

char* ip = "192.168.1.65";
int port = 1234;
char* username="beardface";

LocalClient* myClient;
Protocol* proto;
bool do_check_mail = true;
bool do_update = true;
char* nxt_msg="";

LayerManager manager(2);

WiiTextArea* userListTextArea;
WiiTextArea* messageTextArea;
WiiTextArea* nextMessageTextArea;

Image bg_surface;
Image pointer_surface;
Image font;
GameWindow gwd;

Sprite bg;
Sprite pointer;

mutex_t mutexproto;
mutex_t mutexcheck;
mutex_t mutexgui;

void mssleep(unsigned int mseconds)
{
   unsigned long i = 0;
   while (i < mseconds){i++;};
}

void clean_exit()
{
   LWP_MutexLock (mutexcheck);
   do_check_mail = false;
   LWP_MutexUnlock (mutexcheck);
   mssleep(1000000);
   proto->Send_Quit();
   proto->Disconnect();

   if(proto != NULL) delete proto;
   proto = NULL;

   exit(0);
}

void RenderMainScreen()
{
   /*LWP_MutexLock (mutexgui);*/

   manager.Draw(0,0);
   userListTextArea->Draw();
   messageTextArea->Draw();
   nextMessageTextArea->Draw();

   gwd.Flush();

   do_update = false;
  // LWP_MutexUnlock (mutexgui);
}

void *NetReceive(void *threadid)
{
   int tid;
   tid = (int)threadid;
   int my_uid;

   LWP_MutexLock (mutexcheck);
   while(do_check_mail)
   {
      LWP_MutexUnlock (mutexcheck);
      mssleep(5000);
      LWP_MutexLock (mutexproto);
      switch(proto->Rcv_Handler())
      {
         case MessageType::UID_RESPONSE:
            if((my_uid = proto->ParseUid(proto->Get_Rcv_Buffer())) != -1)
            {
               myClient->Set_Uid(my_uid);
            }
            else printf("Failed (Invalid Uid Returned [%d]).\n",my_uid);
            break;
         case MessageType::MAIL_RESPONSE:
            messageTextArea->Append(proto->ParseMessages(proto->Get_Rcv_Buffer()));
            LWP_MutexLock (mutexgui);
            do_update = true;
            LWP_MutexUnlock (mutexgui);
            proto->Clear_Rcv_Buffer();
            break;
         case MessageType::USER_LIST_RESPONSE:
            userListTextArea->Clear();
            userListTextArea->Append(proto->ParseUsers(proto->Get_Rcv_Buffer()));
            LWP_MutexLock (mutexgui);
            do_update = true;
            LWP_MutexUnlock (mutexgui);
            proto->Clear_Rcv_Buffer();
            break;
         case MessageType::ALL_DATA_RESPONSE:
            printf("All Data Response.\n");
            break;
         case MessageType::NONE:
            proto->Clear_Rcv_Buffer();
            break;
         default:
            break;
      }
      LWP_MutexUnlock (mutexproto);
      LWP_MutexLock (mutexcheck);
   }
   LWP_MutexUnlock (mutexcheck);
   LWP_SuspendThread(tid);
}

void *CheckMail(void *threadid)
{
   int tid;
   tid = (int)threadid;
   
   LWP_MutexLock (mutexcheck);
   while(do_check_mail)
   {
      LWP_MutexUnlock (mutexcheck);
      mssleep(500000);
      LWP_MutexLock (mutexproto);
      proto->Rqst_Mail();
      LWP_MutexUnlock (mutexproto);
      LWP_MutexLock (mutexcheck);
   }
   LWP_MutexUnlock (mutexcheck);
   LWP_SuspendThread(tid);
}

void *UpdateUserList(void *threadid)
{
   int tid;
   tid = (int)threadid;

   LWP_MutexLock (mutexcheck);
   while(do_check_mail)
   {
      LWP_MutexUnlock (mutexcheck);
      mssleep(1000000);
      LWP_MutexLock (mutexproto);
      proto->Rqst_User_List();
      LWP_MutexUnlock (mutexproto);
      LWP_MutexLock (mutexcheck);
   }
   LWP_MutexUnlock (mutexcheck);
   LWP_SuspendThread(tid);

}


void init_wii()
{
        printf("init fat.\n");
        //mssleep(100000);
	WPAD_Init();
	gwd.InitVideo();
	fatInitDefault();
	WPAD_SetDataFormat(WPAD_CHAN_0, WPAD_FMT_BTNS_ACC_IR);
        //mssleep(100000);
}

void wait_for_press()
{
	while(1) {

		// Call WPAD_ScanPads each loop, this reads the latest controller states
		WPAD_ScanPads();

		// WPAD_ButtonsDown tells us which buttons were pressed in this loop
		// this is a "one shot" state which will not fire again until the button has been released
		u32 pressed = WPAD_ButtonsDown(0);

		// We return to the launcher application via exit
		if ( pressed & WPAD_BUTTON_A ) break;

		if ( pressed & WPAD_BUTTON_HOME ) exit(0);

		// Wait for the next frame
		VIDEO_WaitVSync();
	}
}

int main(int argc, char*argv[]) {

   int received = 0;
   int my_uid = -1;
   bool done = false;
   lwp_t threads[NUM_THREADS];
   int rc, t;
   std::string line;
   printf("Init wii.\n");
   init_wii();


   printf("Load Image\n");
   if(bg_surface.LoadImage("/apps/wiichat/img/bg.png") != IMG_LOAD_ERROR_NONE)exit(0);
   bg.SetImage(&bg_surface);
   bg.SetPosition(0,0);

   printf("Load Image\n");
   if(pointer_surface.LoadImage("/apps/wiichat/img/pointer.png") != IMG_LOAD_ERROR_NONE)exit(0);
   pointer.SetImage(&pointer_surface);
   pointer.SetPosition(0,0);

   printf("Load Image\n");
   if(font.LoadImage("/apps/wiichat/img/font.png") != IMG_LOAD_ERROR_NONE)exit(0);

   printf("Set up manager\n");
   manager.Append(&pointer);
   manager.Append(&bg);

   printf("Init kbd.\n");  
   OnScreenKeyboard* osk;

   printf("Init kbd (Struct).\n");  
   osk = new OnScreenKeyboard(&gwd, "/apps/wiichat/config/key_config.xml");

   userListTextArea    = new WiiTextArea(483, 44, 30, 20,  &font);
   messageTextArea     = new WiiTextArea(26, 256, 11, 120, &font);
   nextMessageTextArea = new WiiTextArea(18, 436, 1, 100,  &font);

   printf("init proto.\n");  
   proto = new Protocol();

   myClient = new LocalClient(-1,username,0,0,0);
   
   messageTextArea->Append("Connecting....\n");
   RenderMainScreen();

   printf("try to connect.\n");  
   while(!(proto->Connect(ip,port))){
      messageTextArea->Append("Failed to connect. Press A to try again.\n");
      RenderMainScreen();
      wait_for_press();
   }

   RenderMainScreen();
   printf("connected.\n");
   messageTextArea->Append("Connected.\n");
   RenderMainScreen();

   messageTextArea->Append("Getting Uid...\n");
   RenderMainScreen();
   proto->Get_Uid();
   
   messageTextArea->Append("Updating Name...");
   RenderMainScreen();
   if(proto->Update_Name(myClient->Get_Name())) messageTextArea->Append("Success.\n");
   else messageTextArea->Append("Failed.\n");

   messageTextArea->Append("Updating Pos...");
   RenderMainScreen();

   if(proto->Update_Pos(myClient->X(),myClient->Y(),myClient->Z())) messageTextArea->Append("Success.\n");
   else messageTextArea->Append("Failed.\n");

   RenderMainScreen();
   if(proto->Rqst_User_List()) printf("Success.\n");
   else printf("Failed.\n");
/*
    rc = LWP_CreateThread(&threads[0], NetReceive, NULL, NULL, 0, 1);
    if(rc!=0) {
      printf("ERROR; return code from pthread_create() is %d\n", rc);
      exit(-1);
   }

    rc = LWP_CreateThread(&threads[1], CheckMail, NULL, NULL, 0, 2);
   if(rc!=0) {
      printf("ERROR; return code from pthread_create() is %d\n", rc);
      exit(-1);
   }

    rc = LWP_CreateThread(&threads[2], UpdateUserList, NULL, NULL, 0, 3);
    if(rc!=0) {
      printf("ERROR; return code from pthread_create() is %d\n", rc);
      exit(-1);
   }
  */ 
   while(1)
   {
      WPAD_ScanPads();
      u32 pressed = WPAD_ButtonsDown(WPAD_CHAN_0);

      if(pressed & WPAD_BUTTON_HOME)exit(0);//clean_exit();

      ir_t ir;
      WPAD_IR(WPAD_CHAN_0, &ir);
      pointer.SetPosition(ir.sx-200, ir.sy-250);
      pointer.SetRotation(ir.angle/2);

      if(pressed & WPAD_BUTTON_A)
      {
	    int t_x = ir.sx-200;
            int t_y = ir.sy-250;

            if(t_x > 10 && t_x < 200 && t_y > 35 && t_y < 200)
            {
               if(t_y > 35 && t_y < 75)
               {
                  //button 1 pressed (Change_name)
                  char* tname = osk->EditString(myClient->Get_Name());
                  if(tname != NULL) 
                  {
                         myClient->Update_Name(tname);
			// LWP_MutexLock (mutexproto);
			// proto->Update_Name(tname);
			// LWP_MutexUnlock (mutexproto);
                  
                  }
               }
               else if(t_y > 78 && t_y < 116)
               {
                  //button 2 pressed
               }
               else if(t_y > 120 && t_y < 158)
               {
                  //button 3 (About) pressed
               }
               else if(t_y > 161 && t_y < 200)
               {
                  //button 4 (Quit) pressed
		 /*LWP_MutexLock (mutexcheck);
		 do_check_mail = false;
		 LWP_MutexUnlock (mutexcheck);
		 sleep(2);
		 LWP_MutexLock (mutexproto);
		 proto->Send_Quit();
		 LWP_MutexUnlock (mutexproto);*/
		 done = true;
                 exit(0);
               }
            }
            else if(t_x > 15 && t_x < 547 && t_y > 431 && t_y < 465)
            {
                //clicked edit box
                nxt_msg = osk->EditString(nxt_msg);
                nextMessageTextArea->Clear();
                nextMessageTextArea->Append(nxt_msg);
            }
            else if(t_x > 555 && t_x < 626 && t_y > 431 && t_y < 465)
            {
                //msg send
		// LWP_MutexLock (mutexproto);
		// proto->Send_Message(nxt_msg,"global"); 
                // nxt_msg = "\0";
		// LWP_MutexUnlock (mutexproto);

                 nextMessageTextArea->Clear();
            }   
      }

      RenderMainScreen();
   }   

//   proto->Disconnect();

//   if(proto != NULL) delete proto;
//   proto = NULL;

   exit(0);
}
